<?php
require_once MORIARTY_DIR.'credentials.class.php';
require_once MORIARTY_DIR.'networkresource.class.php';
require_once MORIARTY_DIR.'fieldpredicatemap.class.php';
require_once MORIARTY_DIR.'queryprofile.class.php';
require_once MORIARTY_DIR.'changeset.class.php';
require_once APPLICATION_PATH.'/utils/JobList.class.php';
require_once APPLICATION_PATH.'/utils/Utils.php';

class StoreUtils
{
    private $store;
    private $user;
    private $password;

    public function __construct($storeUri, $user=null, $password=null)
    {
        $store = new Store($storeUri, new Credentials($user, $password));
        $sparql = $store->get_sparql_service();
        $response = $sparql->graph('describe <http://faketriplefoofoofoo>');

        if ($response->status_code == '401')
        {
            throw new StoreAuthenticationException('This store requires credentials to access it');
        }
        elseif ($response->status_code == '404')
        {
            throw new InvalidStoreException('The requested store does not exist');
        }
        elseif (!$response->is_success())
        {
            throw new Zend_Exception('Failed to connect to store: '.$response->to_string());
        }

        $this->store = $store;
        $this->user = $user;
        $this->password = $password;
    }

    public function getJobQueue()
    {
        return $this->store->get_job_queue();
    }

    public function getJobQueueUris()
    {
        return $this->getJobQueue()->get_item_uris();
    }

    public function clearStore()
    {
        $jobQueue = $this->store->get_job_queue();
        $shortName = Utils::getShortStoreName($this->store->uri);
        $response = $jobQueue->schedule_reset_data(null, 'Clear store '.$shortName);

        if ($response->is_success())
        {
            $jobUri = $response->headers['location'];
            Zend_Registry::get('logger')->info("Clearing store: {$shortName} ({$jobUri})");

            $jobList = new JobList();
            $jobList->addJob('Clear '.$shortName, $jobUri);

            return $jobUri;
        }
        else
        {
            throw new Zend_Exception('Error attempting to clear store: '.$shortName);
        }
    }

    public function search($searchString, $offset=0, $max=10)
    {
        return $this->store->get_contentbox()->search_to_resource_list($searchString, $max, $offset);
    }

    public function reindexStore()
    {
        $jobQueue = $this->store->get_job_queue();
        $shortName = Utils::getShortStoreName($this->store->uri);
        $response = $jobQueue->schedule_reindex();

        if ($response->is_success())
        {
            $jobUri = $response->headers['location'];
            Zend_Registry::get('logger')->info("Re-indexing store: {$shortName} ({$jobUri})");

            $jobList = new JobList();
            $jobList->addJob('Re-index '.$shortName, $jobUri);
            return $jobUri;
        }
        else
        {
            throw new Zend_Exception('Error attempting to re-index store: '.$shortName);
        }
    }

    public function getSnapshots()
    {
    	$uri = $this->store->uri.'/snapshots';
    	$request = new HttpRequest('GET', $uri, new Credentials($this->user, $this->password));
    	$response = $request->execute();

    	$snapshotURIs = array();

    	if ($response->is_success())
    	{
    		$dom = new DOMDocument();
    		if ($dom->loadHTML($response->body) === FALSE)
    		{
    			throw new Zend_Exceptio('Error parsing DOM of response');
    		}

    		$xpath = new DOMXPath($dom);
    		$nodelist = $xpath->query('//a');
    		foreach ($nodelist as $node)
    		{
    			$href = $node->getAttribute('href');
    			$extension = substr($href, strrpos($href, '.')+1);

    			if ($extension == 'tar')
    			{
    				$snapshotURIs[] = $href;
    			}
    		}

    		return $snapshotURIs;
    	}
    	else
    	{
    		throw new Zend_Exception('Error getting list of snapshots');
    	}
    }

    public function restore($snapshotUri=null)
    {
		if (empty($snapshotUri))
		{
			throw new Zend_Exception("Parameter 'snapshotUri' is required");
		}

        $jobQueue = $this->store->get_job_queue();
        $shortName = Utils::getShortStoreName($this->store->uri);
        $response = $jobQueue->schedule_restore($snapshotUri);

        if ($response->is_success())
        {
            $jobUri = $response->headers['location'];
            Zend_Registry::get('logger')->info("Restore store: {$shortName} ({$jobUri})");

//            $jobList = new JobList();
//            $jobList->addJob('Snapshot '.$shortName, $jobUri);

            return $jobUri;
        }
        else
        {
            Zend_Registry::get('logger')->error("Error restoring snapshot of {$shortName}: ".$response->to_string());
        	throw new Zend_Exception('Error restoring snapshot of '.$shortName);
        }
    }

    public function snapshot()
    {
        $jobQueue = $this->store->get_job_queue();
        $shortName = Utils::getShortStoreName($this->store->uri);
        $response = $jobQueue->schedule_snapshot();

        if ($response->is_success())
        {
            $jobUri = $response->headers['location'];
            Zend_Registry::get('logger')->info("Snapshot store: {$shortName} ({$jobUri})");

//            $jobList = new JobList();
//            $jobList->addJob('Snapshot '.$shortName, $jobUri);

            return $jobUri;
        }
        else
        {
            Zend_Registry::get('logger')->error("Error performaing store snapshot of {$shortName}: ".$response->to_string());
        	throw new Zend_Exception('Error attempting to create snapshot of store: '.$shortName);
        }
    }

    public function getFPMap()
    {
        //TODO When the platform exposes the real location of the Field Predicate Nap then remove the double-checking below.
        $oldLocation = $this->store->uri.'/indexes/metaboxIndex/fpmaps/fpmap';
        $newLocation = $this->store->uri.'/config/fpmaps/1';

        $fpmap = new FieldPredicateMap($oldLocation, new Credentials($this->user, $this->password));
        $response = $fpmap->get_from_network();

        if ($response->is_success())
        {
            return array('location'=>$oldLocation, 'data'=>$response->body);
        }

        $fpmap = new FieldPredicateMap($newLocation, new Credentials($this->user, $this->password));
        $response = $fpmap->get_from_network();

        if ($response->is_success())
        {
            return array('location'=>$newLocation, 'data'=>$response->body);
        }

        throw new Exception("Cannot find Field Predicate Map in either of the expected locations");
    }

    public function putToLocation($location, $data)
    {
        $networkResource = new NetworkResource($location, new Credentials($this->user, $this->password));
        $networkResource->from_rdfxml($data);

        $response = $networkResource->put_to_network();

        return $response;
    }

    public function getQueryProfile()
    {
        //TODO When the platform exposes the real location of the Query Profile then remove the double-checking below.
        $oldLocation = $this->store->uri.'/indexes/metaboxIndex/queryprofiles/default';
        $newLocation = $this->store->uri.'/config/queryprofiles/1';

        $queryProfile = new QueryProfile($oldLocation, new Credentials($this->user, $this->password));
        $response = $queryProfile->get_from_network();

        if ($response->is_success())
        {
            return array('location'=>$oldLocation, 'data'=>$response->body);
        }

        $queryProfile = new QueryProfile($newLocation, new Credentials($this->user, $this->password));
        $response = $queryProfile->get_from_network();

        if ($response->is_success())
        {
            return array('location'=>$newLocation, 'data'=>$response->body);
        }

        throw new Exception("Cannot find Query Profile in either of the expected locations");
    }

    public function getReadStatus()
    {
        $config = $this->store->get_config();
        return $config->get_access_status();
    }

    public function applyChangeset(ChangeSet $cs)
    {
    	$response = $this->store->get_metabox()->apply_changeset($cs);

    	if (!$response->is_success())
    	{
    		throw new Zend_Exception('Error saving changeset:' . $response->to_string(), $response->status_code);
    	}
    }

    public function describe($uri)
    {
        $query = "DESCRIBE <{$uri}>";
        return $this->query($query);
    }

    public function select($query)
    {
        $sparql = $this->store->get_sparql_service();
        $response = $sparql->select($query);

        if ($response->is_success())
        {
            $data = $sparql->parse_select_results($response->body);
            return $data;
        }
        else
        {
            throw new Zend_Exception('Error performing query : '.$response->to_string(), $response->status_code);
        }
    }

    public function query($query)
    {
        $sparql = $this->store->get_sparql_service();

        $response = $sparql->graph($query);

        if ($response->is_success())
        {
            return $response->body;
        }
        else
        {
            throw new Zend_Exception('Error performing query : '.$response->to_string(), $response->status_code);
        }
    }

    public function importFile($fileName)
    {
        $fileContents = file_get_contents($fileName);
        if ($fileContents === FALSE)
        {
            throw new Zend_Exception('Cannot find file: '.$fileName);
        }

        $response = $this->store->get_metabox()->submit_rdfxml($fileContents);

        unlink($fileName);

        if (!$response->is_success())
        {
            throw new Zend_Exception('Failed to import file: '.$fileName.' ('.$response->to_string().')');
        }
    }

    public function getDistinctTypes()
    {
        $query = 'select distinct ?type where {?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ?type}';
        return $this->select($query);
    }

    public function getTypeCount($typeUri)
    {
        $query = "select (count(?s) as ?count) where {?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <{$typeUri}>}";
        $countData = $this->select($query);

        $count = $countData[0]['count']['value'];
        return $count;
    }
}

class StoreAuthenticationException extends Exception { }

class InvalidStoreException extends Exception { }
